package edu.spsu.cs4253;

import java.lang.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class Peer2PeerServer implements Runnable {

	//States of Connections
   public final static int NULL = 0;
   public final static int DISCONNECTED = 1;
   public final static int DISCONNECTING = 2;
   public final static int BEGIN_CONNECT = 3;
   public final static int CONNECTED = 4;
   public final static String statusMessages[] = {
      " Error! Could not connect!", " Disconnected",
      " Disconnecting...", " Connecting...", " Connected"
   };

   public final static String END_CHAT_SESSION = new Character((char)0).toString(); 
   public static int connectionStatus = DISCONNECTED;
   public static String statusString = statusMessages[connectionStatus];
   
   //IPs, Port, sending stuff
   public static String serverIP = "localhost";
   public static int port;
   public static boolean isHost = true;
   public static StringBuffer toAppend = new StringBuffer("");
   public static StringBuffer toSend = new StringBuffer("");

   //GUI
   public static JFrame mainFrame = null;
   public static JTextArea chatText = null;
   public static JTextField chatLine = null;
   public static JPanel statusBar = null;
   public static JLabel statusField = null;
   public static JTextField statusColor = null;
   public static JTextField ipField = null;
   public static JTextField portField = null;
   public static JRadioButton hostOption = null;
   public static JButton connectButton = null;
   public static JButton disconnectButton = null;

   //Sockets, Inputs, Outputs
   public static ServerSocket hostServer = null;
   public static Socket socket = null;
   public static BufferedReader in = null;
   public static PrintWriter out = null;
   
   public final static Peer2PeerServer tcpObj = new Peer2PeerServer(socket);


   //Left side of GUI
   private static JPanel initOptionsPane() {
      JPanel pane = null;
      ActionAdapter buttonListener = null;

     
      JPanel optionsPane = new JPanel(new GridLayout(4, 1));

      //Server IP Input Field
      pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      pane.add(new JLabel("Server IP:"));
      ipField = new JTextField(10); ipField.setText(serverIP);
      ipField.setEnabled(false);
      ipField.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
               ipField.selectAll();
            
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  serverIP = ipField.getText();
               }
            }
         });
      pane.add(ipField);
      optionsPane.add(pane);

      //Port Number Input Field
      pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      pane.add(new JLabel("Port:"));
      portField = new JTextField(10); portField.setEditable(true);
      portField.setText((new Integer(port)).toString());
      portField.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
            
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  int temp;
                  try {
                     temp = Integer.parseInt(portField.getText());
                     port = temp;
                  }
                  catch (NumberFormatException nfe) {
                     portField.setText((new Integer(port)).toString());
                     mainFrame.repaint();
                  }
               }
            }
         });
      pane.add(portField);
      optionsPane.add(pane);

      
      buttonListener = new ActionAdapter() {
            public void actionPerformed(ActionEvent e) {
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  isHost = e.getActionCommand().equals("host");

                 
                  if (isHost) {
                     ipField.setEnabled(false);
                     ipField.setText("localhost");
                     serverIP = "localhost";
                  }
                  else {
                     ipField.setEnabled(true);
                  }
               }
            }
         };
      //Radio Buttons to choose between Server or Client
      ButtonGroup bg = new ButtonGroup();
      hostOption = new JRadioButton("Server", true);
      hostOption.setMnemonic(KeyEvent.VK_H);
      hostOption.setActionCommand("server");
      hostOption.addActionListener(buttonListener);
      bg.add(hostOption);
      pane = new JPanel(new GridLayout(1, 2));
      pane.add(hostOption);
      optionsPane.add(pane);

      //Buttons for Connecting and Disconnecting 
      JPanel buttonPane = new JPanel(new GridLayout(1, 2));
      buttonListener = new ActionAdapter() {
            public void actionPerformed(ActionEvent e) {
               // Starts to Connect to Server
               if (e.getActionCommand().equals("connect")) {
                  changeStatusGUI(BEGIN_CONNECT, true);
               }
               
               else {
                  changeStatusGUI(DISCONNECTING, true);
               }
            }
         };
      connectButton = new JButton("Connect");
      connectButton.setMnemonic(KeyEvent.VK_C);
      connectButton.setActionCommand("connect");
      connectButton.addActionListener(buttonListener);
      connectButton.setEnabled(true);
      disconnectButton = new JButton("Disconnect");
      disconnectButton.setMnemonic(KeyEvent.VK_D);
      disconnectButton.setActionCommand("disconnect");
      disconnectButton.addActionListener(buttonListener);
      disconnectButton.setEnabled(false);
      buttonPane.add(connectButton);
      buttonPane.add(disconnectButton);
      optionsPane.add(buttonPane);

      return optionsPane;
   }
   
   //Starts building the GUI 
   private static void initGUI() {
     
      statusField = new JLabel();
      statusField.setText(statusMessages[DISCONNECTED]);
      statusColor = new JTextField(1);
      statusColor.setBackground(Color.red);
      statusColor.setEditable(false);
      statusBar = new JPanel(new BorderLayout());
      statusBar.add(statusColor, BorderLayout.WEST);
      statusBar.add(statusField, BorderLayout.CENTER);

     
      JPanel optionsPane = initOptionsPane();

      //Right side of the GUI:Chat 
      JPanel chatPane = new JPanel(new BorderLayout());
      chatText = new JTextArea(10, 20);
      chatText.setLineWrap(true);
      chatText.setEditable(false);
      chatText.setForeground(Color.blue);
      JScrollPane chatTextPane = new JScrollPane(chatText,
         JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
         JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      chatLine = new JTextField();
      chatLine.setEnabled(false);
      chatLine.addActionListener(new ActionAdapter() {
            public void actionPerformed(ActionEvent e) {
               String s = chatLine.getText();
               if (!s.equals("")) {
                  appendToChatBox("OUTGOING: " + s + "\n");
                  chatLine.selectAll();

                  
                  sendString(s);
               }
            }
         });
      //chatPane.add(chatLine, BorderLayout.SOUTH);
      chatPane.add(chatTextPane, BorderLayout.CENTER);
      chatPane.setPreferredSize(new Dimension(200, 200));

      
      JPanel mainPane = new JPanel(new BorderLayout());
      mainPane.add(statusBar, BorderLayout.SOUTH);
      mainPane.add(optionsPane, BorderLayout.WEST);
      mainPane.add(chatPane, BorderLayout.CENTER);

      mainFrame = new JFrame("Peer 2 Peer Server");
      mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      mainFrame.setContentPane(mainPane);
      mainFrame.setSize(mainFrame.getPreferredSize());
      mainFrame.setLocation(200, 200);
      mainFrame.pack();
      mainFrame.setVisible(true);
   }

   //Checks Status of Connection...Threaded
   private static void changeStatus(int newConnectStatus, boolean noError) {

      if (newConnectStatus != NULL) {
         connectionStatus = newConnectStatus;
      }


      if (noError) {
         statusString = statusMessages[connectionStatus];
      }

      else {
         statusString = statusMessages[NULL];
      }

      SwingUtilities.invokeLater(tcpObj);
   }

   //Checks Status of Connection...Non-Threaded
   private static void changeStatusGUI(int newConnectStatus, boolean noError) {

      if (newConnectStatus != NULL) {
         connectionStatus = newConnectStatus;
      }


      if (noError) {
         statusString = statusMessages[connectionStatus];
      }
      
      else {
         statusString = statusMessages[NULL];
      }


      tcpObj.run();
   }

   //Displays on chat box
   private static void appendToChatBox(String s) {
      synchronized (toAppend) {
         toAppend.append(s);
      }
   }

   //Sends to the other peer
   private static void sendString(String s) {
      synchronized (toSend) {
         toSend.append(s + "\n");
      }
   }

  //Closes the Sockets
   private static void closeSockets() {
      try {
         if (hostServer != null) {
            hostServer.close();
            hostServer = null;
         }
      }
      catch (IOException e) { hostServer = null; }

      try {
         if (socket != null) {
            socket.close();
            socket = null;
         }
      }
      catch (IOException e) { socket = null; }

      try {
         if (in != null) {
            in.close();
            in = null;
         }
      }
      catch (IOException e) { in = null; }

      if (out != null) {
         out.close();
         out = null;
      }
   }


   //Controls the animations in the GUI
   public void run() {
      switch (connectionStatus) {
      case DISCONNECTED:
         connectButton.setEnabled(true);
         disconnectButton.setEnabled(false);
         ipField.setEnabled(true);
         portField.setEnabled(true);
         hostOption.setEnabled(true);
         chatLine.setText(""); chatLine.setEnabled(false);
         statusColor.setBackground(Color.red);
         break;

      case DISCONNECTING:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(false);
         ipField.setEnabled(false);
         portField.setEnabled(false);
         hostOption.setEnabled(false);
         chatLine.setEnabled(false);
         statusColor.setBackground(Color.orange);
         break;

      case CONNECTED:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(true);
         ipField.setEnabled(false);
         portField.setEnabled(false);
         hostOption.setEnabled(false);
         chatLine.setEnabled(true);
         statusColor.setBackground(Color.green);
         break;

      case BEGIN_CONNECT:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(false);
         ipField.setEnabled(false);
         portField.setEnabled(false);
         hostOption.setEnabled(false);
         chatLine.setEnabled(false);
         chatLine.grabFocus();
         statusColor.setBackground(Color.orange);
         break;
      }

    
      ipField.setText(serverIP);
      portField.setText((new Integer(port)).toString());
      hostOption.setSelected(isHost);
      statusField.setText(statusString);
      chatText.append(toAppend.toString());
      toAppend.setLength(0);

      mainFrame.repaint();
   }

 public static File getFile (String fileName) throws Exception {
	 File file = null;
	 int fileSize = 6022386;
	 byte[] buffer = new byte[fileSize];
	 
	 file = new File("C:\\Users\\kdebrito\\Documents\\" + fileName);
	 File encryptedFile = new File("C:\\Users\\kdebrito\\Documents\\" + "Encrypted.mp3");
	 File decryptedFile = null;
	
	 InputStream in = socket.getInputStream();
	 FileOutputStream out = new FileOutputStream(file);
	 BufferedOutputStream bufferedOut = new BufferedOutputStream(out);
	 int bytesRead = in.read(buffer, 0, buffer.length);
	 int current = bytesRead;
	 
	 do {
		 bytesRead = in.read(buffer, current, (buffer.length-current));
		 if(bytesRead >= 0)
			 current += bytesRead;
	 }
	 
	 while (bytesRead > -1);
	 
	 System.out.println("BYTES RECEIVED:" + bytesRead);
	 bufferedOut.write(buffer, 0, current);
	 bufferedOut.flush();
	 bufferedOut.close();

	 /*FileOutputStream out = new FileOutputStream(file);
	 String content = "Hello";
	 byte[] buffer = new byte[1024*1024];
	 int bytesReceived = 0;
	 byte[] bfile = content.getBytes();
	 
	 
	 while((bytesReceived = in.read(buffer)) > 0) {
		 out.write(buffer, 0, bytesReceived);
		 System.out.println("BYTES RECEIVED: " + bytesReceived);
		 
		 break;
	 }
	
	out.flush();
	out.close();*/
 
	Encryptor e = new Encryptor(file, encryptedFile, decryptedFile, "password1");
	try {
		e.encrypt();
	} catch (Throwable e1) {
		
		e1.printStackTrace();
	}
	 
	 return file;
 }
 
 public static void sendtoClient (File file) throws Exception {
	 
	 OutputStream out = socket.getOutputStream();
	 InputStream in = socket.getInputStream();
	 byte[] buffer = new byte[1024*1024];
	 int bytesRead = 0;
	 
	 while((bytesRead = in.read(buffer))>0) {
		 out.write(buffer, 0, buffer.length);
	 }
	 
	 System.out.println("BYTES SENT......");
	 in.close();
	
 }
 
 public Peer2PeerServer (Socket clientSocket) {
	 
	 socket = clientSocket;
	
 }
   //This is all the logic for sending, receiving, and creating the sockets. 
   public static void main(String args[]) {
      String s;
      String fileName = "data.mp3";
      
      

      initGUI();

      while (true) {
         try { 
            Thread.sleep(10);
         }
         catch (InterruptedException e) {}

         switch (connectionStatus) {
         case BEGIN_CONNECT:
            try {
               
            	
            	// If radio button is on host. The computer will be the server
              
                  hostServer = new ServerSocket(port);
                  
                  while (true) {
                  socket = Peer2PeerServer.hostServer.accept();
                  System.out.println("CONNECTED TO: " + socket.getInetAddress() + " PORT: " + socket.getPort());

               //Gets input from user
               in = new BufferedReader(new 
                  InputStreamReader(socket.getInputStream()));
               
               //Gets output 
               out = new PrintWriter(socket.getOutputStream(), true);
               changeStatus(CONNECTED, true);
            }
            }
        
            //Closes sockets if any problems 
            catch (IOException e) {
               closeSockets();
               changeStatus(DISCONNECTED, false);
            }
            break;

         case CONNECTED:
            try {
            	
               // Send data
               if (toSend.length() != 0) {
                  out.print(toSend); out.flush();
                  toSend.setLength(0);
                  changeStatus(NULL, true);
               }
               
               try {
            	
				getFile(fileName);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
               // Receives data
               if (in.ready()) {
                  s = in.readLine();
                  if ((s != null) &&  (s.length() != 0)) {
                   
                     if (s.equals(END_CHAT_SESSION)) {
                        changeStatus(DISCONNECTING, true);
                     }

                     
                     else {
                        appendToChatBox(s + "\n");
                        changeStatus(NULL, true);
                     }
                  }
               }
            }
            catch (IOException e) {
               closeSockets();
               changeStatus(DISCONNECTED, false);
            }
            break;

         case DISCONNECTING:
           
            out.print(END_CHAT_SESSION); 
            out.flush();

            
            closeSockets();
            changeStatus(DISCONNECTED, true);
            break;

         default: break; 
         }
      }
   }
}



